Extract SqlBulkCopy column names using dynamic SQL#4092
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates SqlBulkCopy’s target-column discovery query to use dynamic SQL so that referencing sys.all_columns.graph_type doesn’t cause compilation failures on SQL Server 2016, addressing regression #3714.
Changes:
- Reworks
SqlBulkCopy.CreateInitialQuery()to build and execute thesys.all_columnscolumn-name query viasp_executesql. - Adjusts manual test SQL statistics expectations to account for the additional statements executed.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReader.cs | Updates expected SelectCount / SelectRows due to extra statements from dynamic SQL. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs | Uses dynamic SQL for column-name extraction to avoid SQL 2016 compilation errors when graph_type is absent. |
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
Show resolved
Hide resolved
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4092 +/- ##
==========================================
- Coverage 73.22% 66.51% -6.72%
==========================================
Files 280 274 -6
Lines 43000 65785 +22785
==========================================
+ Hits 31486 43754 +12268
- Misses 11514 22031 +10517
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlGraphTables.cs
Show resolved
Hide resolved
5ef395f to
1be29d6
Compare
src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
Show resolved
Hide resolved
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
Show resolved
Hide resolved
priyankatiwari08
left a comment
There was a problem hiding this comment.
Looks good to me, Just added a suggestion to add comment related to explaining why CatalogName is interpolated directly into the dynamic SQL string rather than parameterized
70269ce
|
Addressed both open review comments in 70269ce:
|
This enables the query to compile correctly when the graph_type column is missing
- Fix SQLServerVersion lazy-init: change field initializer from string.Empty to null so the ??= null-coalescing assignment actually triggers. This makes IsAtLeastSQL2017/IsAtLeastSQL2019 work correctly. - Add inline comment explaining why CatalogName and escapedObjectName are interpolated directly into the dynamic SQL string (SQL Server does not allow parameterizing identifiers).
70269ce to
29a3aff
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTestWithTdsServer.cs:97
ForceEncryptionRegistryPathno longer handles SQL Server 2016 (major 13). On SQL 2016 it will returnstring.Empty, andDispose()later passes that value toRemoveForceEncryptionFromRegistryPath, which may end up opening/setting values on an unintended registry key (or throwing). Add anIsSQL2016()branch that returns the MSSQL13 registry path, and/or defensively avoid calling registry cleanup when the path is empty.
if (DataTestUtility.IsSQL2022())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL16.{s_instanceName}\MSSQLSERVER\SuperSocketNetLib";
}
if (DataTestUtility.IsSQL2019())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.{s_instanceName}\MSSQLSERVER\SuperSocketNetLib";
}
if (DataTestUtility.IsSQL2017())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.{s_instanceName}\MSSQLSERVER\SuperSocketNetLib";
}
return string.Empty;
}
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTest.cs:59
ForceEncryptionRegistryPathno longer has an SQL Server 2016 (major 13) mapping; it falls back tostring.Empty. This means the test will skip certificate/registry cleanup inDispose()on SQL 2016 and can leave machine-level state behind. Add anIsSQL2016()branch returning the MSSQL13 registry path so cleanup works on SQL 2016 as well.
if (DataTestUtility.IsSQL2022())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL16.{InstanceName}\MSSQLSERVER\SuperSocketNetLib";
}
if (DataTestUtility.IsSQL2019())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.{InstanceName}\MSSQLSERVER\SuperSocketNetLib";
}
if (DataTestUtility.IsSQL2017())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.{InstanceName}\MSSQLSERVER\SuperSocketNetLib";
}
return string.Empty;
}
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTestWithTdsServer.cs:96
- ForceEncryptionRegistryPath no longer returns a path for SQL Server 2016 (major 13). On SQL 2016 this will return string.Empty, but Dispose() later calls RemoveForceEncryptionFromRegistryPath(ForceEncryptionRegistryPath) without checking for empty, which can lead to attempting to open/set values on the HKLM root key (or throwing). Add an IsSQL2016() branch mapping to MSSQL13.* or guard against empty before using the path.
if (DataTestUtility.IsSQL2017())
{
return $@"SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.{s_instanceName}\MSSQLSERVER\SuperSocketNetLib";
}
return string.Empty;
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTest.cs
Outdated
Show resolved
Hide resolved
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Description
#3590 allowed SqlBulkCopy to find and work with hidden columns (even if those columns weren't accessible server-side.) It contained a check on the
all_columnsDMV to make sure that we only checked thegraph_typecolumn when querying if that column existed; this was to maintain SQL 2016 compatibility.#3714 highlighted that both queries are compiled and fail at the point of compilation anyway, so this wasn't accomplishing anything. To make this work, we need to use dynamic SQL to run the column queries. This PR does so.
Supersedes #3719. Original PR by @edwardneal. This PR was created as a direct branch of the SqlClient repo so I can run the new SQL 16 and 17 CI pipeline stages manually to prove this fixes the issue.
Issues
Fixes #3714.
Testing
Manual testing of all test cases against a SQL 2016 instance (via the updated CI pipelines).